home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 7
/
Aminet 7 - August 1995.iso
/
Aminet
/
comm
/
tcp
/
AmigaTCP.lha
/
AmigaTCP
/
src
/
ftpserv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-24
|
12KB
|
520 lines
/* FTP Server state machine - see RFC 959 */
#define LINELEN 128 /* Length of command buffer */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
/* Command table */
static char *commands[] = {
"user",
#define USER_CMD 0
"acct",
#define ACCT_CMD 1
"pass",
#define PASS_CMD 2
"type",
#define TYPE_CMD 3
"list",
#define LIST_CMD 4
"cwd",
#define CWD_CMD 5
"dele",
#define DELE_CMD 6
"name",
#define NAME_CMD 7
"quit",
#define QUIT_CMD 8
"retr",
#define RETR_CMD 9
"stor",
#define STOR_CMD 10
"port",
#define PORT_CMD 11
"nlst",
#define NLST_CMD 12
"pwd",
#define PWD_CMD 13
"xpwd", /* For compatibility with 4.2BSD */
#define XPWD_CMD 14
NULLCHAR
};
/* Response messages */
static char banner[] = "220 %s FTP Ready\r\n";
static char badcmd[] = "500 Unknown command\r\n";
static char nopass[] = "202 Password not needed\r\n";
static char logged[] = "230 Logged in\r\n";
static char typeok[] = "200 Type OK\r\n";
static char cwdok[] = "250 CWD OK\r\n";
static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
static char badtype[] = "501 Unknown type\r\n";
static char badport[] = "501 Bad port syntax\r\n";
static char unimp[] = "502 Command not yet implemented\r\n";
static char bye[] = "221 Goodbye!\r\n";
static char nodir[] = "553 Can't read directory\r\n";
static char cantopen[] = "550 Can't open file\r\n";
static char sending[] = "150 Opening data connection for %s %s\r\n";
static char cantmake[] = "553 Can't create file\r\n";
static char portok[] = "200 Port command okay\r\n";
static char rxok[] = "226 File received OK\r\n";
static char txok[] = "226 File sent OK\r\n";
static struct tcb *ftp_tcb;
/* Start up FTP service */
ftp_start(argc,argv)
int argc;
char *argv[];
{
struct socket lsocket;
void r_ftp(),s_ftp();
lsocket.address = ip_addr;
if(argc < 2)
lsocket.port = FTP_PORT;
else
lsocket.port = atoi(argv[1]);
ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_ftp,NULLVFP,s_ftp,0,(int *)NULL);
}
ftp_stop()
{
if(ftp_tcb != NULLTCB)
close_tcp(ftp_tcb);
}
/* FTP server control channel connection state change upcall handler */
static
void
s_ftp(tcb,old,new)
struct tcb *tcb;
char old,new;
{
extern char hostname[];
struct ftp *ftp,*ftp_create();
void ftp_delete();
char *inet_ntoa(),*pwd();
switch(new){
#ifdef QUICKSTART
case SYN_RECEIVED:
#else
case ESTABLISHED:
#endif
if((ftp = ftp_create(LINELEN)) == NULLFTP){
/* No space, kill connection */
close_tcp(tcb);
return;
}
ftp->control = tcb; /* Downward link */
tcb->user = (int *)ftp; /* Upward link */
/* Set default data port */
ftp->port.address = tcb->conn.remote.address;
ftp->port.port = FTPD_PORT;
/* Note current directory */
#ifndef AMIGA
ftp->cd = pwd();
#endif
log(tcb,"open FTP");
tprintf(ftp->control,banner,hostname);
break;
case CLOSE_WAIT:
close_tcp(tcb);
break;
case CLOSED:
log(tcb,"close FTP");
if((ftp = (struct ftp *)tcb->user) != NULLFTP)
ftp_delete(ftp);
/* Check if server is being shut down */
if(tcb == ftp_tcb)
ftp_tcb = NULLTCB;
del_tcp(tcb);
break;
}
}
/* FTP control channel receiver upcall handler */
static
void
r_ftp(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
register struct ftp *ftp;
char *index(),c;
struct mbuf *bp;
void docommand();
if((ftp = (struct ftp *)tcb->user) == NULLFTP){
/* Unknown connection, just kill it */
close_tcp(tcb);
return;
}
switch(ftp->state){
case COMMAND_STATE:
/* Assemble an input line in the session buffer. Return if incomplete */
recv_tcp(tcb,&bp,0);
while(pullup(&bp,&c,1) == 1){
switch(c){
case '\r': /* Strip cr's */
continue;
case '\n': /* Complete line; process it */
ftp->buf[ftp->cnt] = '\0';
docommand(ftp);
ftp->cnt = 0;
break;
default: /* Assemble line */
if(ftp->cnt != LINELEN-1)
ftp->buf[ftp->cnt++] = c;
break;
}
}
/* else no linefeed present yet to terminate command */
break;
case SENDING_STATE:
case RECEIVING_STATE:
/* Leave commands pending on receive queue until
* present command is done
*/
break;
}
}
/* FTP server data channel connection state change upcall handler */
void
s_ftpd(tcb,old,new)
struct tcb *tcb;
char old,new;
{
struct ftp *ftp;
#ifndef CPM
#ifndef AMIGA
char *cdsave;
#endif
#endif
if((ftp = (struct ftp *)tcb->user) == NULLFTP){
/* Unknown connection, kill it */
close_tcp(tcb);
return;
}
switch(new){
case FINWAIT2:
case TIME_WAIT:
if(ftp != NULLFTP && ftp->state == SENDING_STATE){
/* We've received an ack of our FIN, so
* send a completion message on the control channel
*/
ftp->state = COMMAND_STATE;
tprintf(ftp->control,txok);
/* Kick command parser if something is waiting */
if(ftp->control->rcvcnt != 0)
r_ftp(ftp->control,ftp->control->rcvcnt);
}
break;
case CLOSE_WAIT:
close_tcp(tcb);
if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
/* End of file received on incoming file */
#ifdef CPM
if(ftp->type == ASCII_TYPE)
putc(CTLZ,ftp->fp);
#endif
#ifndef CPM
#ifndef AMIGA
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Switch to user's directory*/
#endif
#endif
fclose(ftp->fp);
#ifndef CPM
#ifndef AMIGA
if(cdsave != NULLCHAR){
chdir(cdsave); /* And back */
free(cdsave);
}
#endif
#endif
ftp->fp = NULLFILE;
ftp->state = COMMAND_STATE;
tprintf(ftp->control,rxok);
/* Kick command parser if something is waiting */
if(ftp->control->rcvcnt != 0)
r_ftp(ftp->control,ftp->control->rcvcnt);
}
break;
case CLOSED:
if(ftp != NULLFTP)
ftp->data = NULLTCB;
del_tcp(tcb);
break;
}
}
/* Parse and execute ftp commands */
static
void
docommand(ftp)
register struct ftp *ftp;
{
void r_ftpd(),t_ftpd(),s_ftpd();
char *cmd,*arg,*cp,**cmdp;
char *index(),*malloc(),*strcpy();
struct socket dport;
#ifndef CPM
#ifndef AMIGA
FILE *dir();
char *cdsave;
#endif
#endif
cmd = ftp->buf;
if(ftp->cnt == 0){
/* Can't be a legal FTP command */
tprintf(ftp->control,badcmd);
return;
}
cmd = ftp->buf;
/* Translate entire buffer to lower case */
for(cp = cmd;*cp != '\0';cp++)
*cp = tolower(*cp);
/* Find command in table; if not present, return syntax error */
for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
break;
if(*cmdp == NULLCHAR){
tprintf(ftp->control,badcmd);
return;
}
arg = &cmd[strlen(*cmdp)];
while(*arg == ' ')
arg++;
/* Execute specific command */
switch(cmdp-commands){
case USER_CMD:
if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
close_tcp(ftp->control);
break;
}
strcpy(ftp->username,arg);
tprintf(ftp->control,logged);
break;
case TYPE_CMD:
switch(*arg){
case 'a': /* Ascii */
ftp->type = ASCII_TYPE;
tprintf(ftp->control,typeok);
break;
case 'b': /* Binary */
case 'i': /* Image */
ftp->type = IMAGE_TYPE;
tprintf(ftp->control,typeok);
break;
default: /* Invalid */
tprintf(ftp->control,badtype);
break;
}
break;
case QUIT_CMD:
tprintf(ftp->control,bye);
close_tcp(ftp->control);
break;
case RETR_CMD:
/* Disk operation; return ACK now */
tcp_output(ftp->control);
#ifndef CPM
#ifndef AMIGA
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Switch to user's directory*/
#endif
#endif
ftp->fp = fopen(arg,"r");
#ifndef CPM
#ifndef AMIGA
chdir(cdsave); /* And back */
free(cdsave);
#endif
#endif
if(ftp->fp == NULLFILE){
tprintf(ftp->control,cantopen);
} else {
log(ftp->control,"RETR %s/%s",ftp->cd,arg);
dport.address = ip_addr;
dport.port = FTPD_PORT;
ftp->state = SENDING_STATE;
tprintf(ftp->control,sending,"RETR",arg);
/* This hack is just so we can talk to ourselves */
ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
}
break;
case STOR_CMD:
/* Disk operation; return ACK now */
tcp_output(ftp->control);
#ifndef CPM
#ifndef AMIGA
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Switch to user's directory */
#endif
#endif
ftp->fp = fopen(arg,"w");
#ifndef CPM
#ifndef AMIGA
chdir(cdsave); /* And back */
free(cdsave);
#endif
#endif
if(ftp->fp == NULLFILE){
tprintf(ftp->control,cantmake);
} else {
log(ftp->control,"STOR %s/%s",ftp->cd,arg);
dport.address = ip_addr;
dport.port = FTPD_PORT;
ftp->state = RECEIVING_STATE;
tprintf(ftp->control,sending,"STOR",arg);
/* This hack is just so we can talk to ourselves */
ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
}
break;
case PORT_CMD:
if(pport(&ftp->port,arg) == -1){
tprintf(ftp->control,badport);
} else {
tprintf(ftp->control,portok);
}
break;
/* #ifndef CPM */
#ifndef AMIGA
case LIST_CMD:
/* Disk operation; return ACK now */
tcp_output(ftp->control);
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Switch to user's directory */
ftp->fp = dir(arg,1);
chdir(cdsave); /* And back */
free(cdsave);
if(ftp->fp == NULLFILE){
tprintf(ftp->control,nodir);
break;
}
dport.address = ip_addr;
dport.port = FTPD_PORT;
ftp->state = SENDING_STATE;
tprintf(ftp->control,sending,"LIST",arg);
/* This hack is just so we can talk to ourselves */
ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
break;
case NLST_CMD:
/* Disk operation; return ACK now */
tcp_output(ftp->control);
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Switch to user's directory */
ftp->fp = dir(arg,0);
chdir(cdsave); /* And back */
free(cdsave);
if(ftp->fp == NULLFILE){
tprintf(ftp->control,nodir);
break;
}
dport.address = ip_addr;
dport.port = FTPD_PORT;
ftp->state = SENDING_STATE;
tprintf(ftp->control,sending,"NLST",arg);
/* This hack is just so we can talk to ourselves */
ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
break;
case CWD_CMD:
tcp_output(ftp->control); /* Disk operation; return ACK now */
cdsave = pwd(); /* Save current directory */
chdir(ftp->cd); /* Go to user's context */
if(chdir(arg) == 0){ /* Attempt switch */
/* Succeeded, record in control block */
free(ftp->cd);
ftp->cd = pwd();
tprintf(ftp->control,cwdok);
} else {
/* Failed, don't change anything */
tprintf(ftp->control,nodir);
}
chdir(cdsave); /* Go back */
free(cdsave);
break;
case XPWD_CMD:
case PWD_CMD:
tprintf(ftp->control,pwdmsg,ftp->cd);
break;
#else
case LIST_CMD:
case NLST_CMD:
case CWD_CMD:
case XPWD_CMD:
case PWD_CMD:
#endif
case ACCT_CMD:
case DELE_CMD:
tprintf(ftp->control,unimp);
break;
case PASS_CMD:
tprintf(ftp->control,nopass);
break;
}
}
static
int
pport(sock,arg)
struct socket *sock;
char *arg;
{
int32 n;
int atoi(),i;
n = 0;
for(i=0;i<4;i++){
n = atoi(arg) + (n << 8);
if((arg = index(arg,',')) == NULLCHAR)
return -1;
arg++;
}
sock->address = n;
n = atoi(arg);
if((arg = index(arg,',')) == NULLCHAR)
return -1;
arg++;
n = atoi(arg) + (n << 8);
sock->port = n;
return 0;
}